﻿//Copyright (c) 2010 Christopher E. S. King (http://chriskingconsulting.com)
//
//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 
// (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do 
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using System.Reflection;
using System.Collections;

using King.Extensions;
using King.Text;
using King.Reflection;

namespace King.Language.CSharp.Generator {
#if false
    #region Substitutions (CSSubstitutionType, CSSubstitution)
    internal enum CSSubstitutionType {
        Literal = 1,
        Type,
        AttributeType,
        TypeToIdentifier,
        AttributeGroupTarget,
        Visibility,
    }
    public class CSSubstitution : TextNodes {

        #region Static Public Members
        public static object SubstituteAttributeType(TextNodeContext context, Type type) {
            var name = Regex.Replace(type.Name, "Attribute$", string.Empty);
            var result = new CSTypeBuilder(new CSTypeRef(type, name));
            return result;
        }
        public static object SubstituteType(TextNodeContext context, Type type) {

            object result;
            if (s_typeToTObject.TryGetValue(type, out result))
                return result;

            if (type.HasElementType) {
                string elementType;
                if (type.IsArray)
                    elementType = "[]";
                else if (type.IsPointer)
                    elementType = "*";
                else
                    throw new ArgumentException();

                result = new object[] { SubstituteType(context, type.GetElementType()), elementType };

            } else {

                var isNullable = type.IsSubclassOf(typeof(Nullable)) ||
                    (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>));

                var elementType = type;
                if (isNullable)
                    elementType = type.GetGenericArguments()[0];

                var name = elementType.Name;
                if (elementType.IsGenericType)
                    name = name.Substring(0, name.LastIndexOf("`"));

                result = new CSTypeBuilder() {
                    Name = new CSTypeRef(elementType, name),
                    IsNullable = isNullable,
                    GenericArguments = elementType.GetGenericArguments(),
                };
            }

            s_typeToTObject[type] = result;
            return result;
        }
        public static object SubstituteLiteral(TextNodeContext context, object literal) {
            if (literal is int)
                return literal.ToString();
            if (literal is long)
                return literal.ToString() + "L";
            if (literal is bool)
                return (bool)literal ? CSKeyWord.True : CSKeyWord.False;
            if (literal is Type)
                return new CSTypeOf(literal);
            if (literal is Enum)
                return SubstituteEnum(context, (Enum)literal);
            if (literal is CustomAttributeTypedArgument) {
                var typedArgument = (CustomAttributeTypedArgument)literal;
                if (typedArgument.ArgumentType.IsEnum)
                    return SubstituteEnum(context, (CustomAttributeTypedArgument)literal);
                return new CSAttributeValue(typedArgument.Value);
            }
            return literal;
        }
        public static object SubstituteAttributeGroupTarget(TextNodeContext context, CSAttributeGroupTarget target) {

            if (target == CSAttributeGroupTarget.Event)
                return CSKeyWord.Event;
            if (target == CSAttributeGroupTarget.Field)
                return CSKeyWord.Field;
            if (target == CSAttributeGroupTarget.Method)
                return CSKeyWord.Method;
            if (target == CSAttributeGroupTarget.Module)
                return CSKeyWord.Module;
            if (target == CSAttributeGroupTarget.Param)
                return CSKeyWord.Param;
            if (target == CSAttributeGroupTarget.Property)
                return CSKeyWord.Property;
            if (target == CSAttributeGroupTarget.Return)
                return CSKeyWord.Return;
            if (target == CSAttributeGroupTarget.Type)
                return CSKeyWord.Type;
            if (target == CSAttributeGroupTarget.None)
                return null;

            return target;
        }
        public static object SubstituteVisibility(TextNodeContext context, CSVisibility visibility) {

            if (visibility == CSVisibility.None)
                return null;
            if (visibility == CSVisibility.Public)
                return CSKeyWord.Public;
            if (visibility == CSVisibility.Private)
                return CSKeyWord.Private;
            if (visibility == CSVisibility.Internal)
                return CSKeyWord.Internal;
            if (visibility == CSVisibility.Protected)
                return CSKeyWord.Protected;
            if (visibility == CSVisibility.ProtectedInternal)
                return new[] { CSKeyWord.Protected, " ", CSKeyWord.Internal };

            return visibility;
        }
        public static object SubstituteEnum(TextNodeContext context, Enum target) {
            return SubstituteEnum(context, target.GetType(), target);
        }
        public static object SubstituteEnum(TextNodeContext context, CustomAttributeTypedArgument target) {
            return SubstituteEnum(context, target.ArgumentType, target.Value);
        }
        public static object SubstituteEnum(TextNodeContext context, Type enumType, object target) {

            var enumInfo = EnumInfo.GetEnumInfo(enumType);
            if (enumInfo == null)
                return target;

            if (!enumType.IsDefined(typeof(FlagsAttribute)))
                return new CSEnumValue(
                    new CSSubstitution(CSSubstitutionType.Type) { enumType },
                    enumInfo.GetValue(target).Name
                );

            return new TextJoin(" | ") {
                enumInfo.Values(target)
                    .GreatestCommonSets((l, r) => l.Contains(r))
                    .OrderBy(o => o.Name)
                    .Select(o => new CSEnumValue(enumType, o.Name))
            };
        }
        #endregion

        #region Static Constructor
        static CSSubstitution() {
            s_typeToTObject = new[] {
                new { Key = typeof(void), Value = CSTypeBuilder.Void },
                new { Key = typeof(object), Value = CSTypeBuilder.Object },
                new { Key = typeof(string), Value = CSTypeBuilder.String },

                new { Key = typeof(bool), Value = CSTypeBuilder.Bool },
                new { Key = typeof(sbyte), Value = CSTypeBuilder.SByte },
                new { Key = typeof(short), Value = CSTypeBuilder.Short },
                new { Key = typeof(int), Value = CSTypeBuilder.Int },
                new { Key = typeof(long), Value = CSTypeBuilder.Long },
                new { Key = typeof(byte), Value = CSTypeBuilder.Byte },
                new { Key = typeof(ushort), Value = CSTypeBuilder.UShort },
                new { Key = typeof(uint), Value = CSTypeBuilder.UInt },
                new { Key = typeof(ulong), Value = CSTypeBuilder.ULong },
                new { Key = typeof(float), Value = CSTypeBuilder.Float },
                new { Key = typeof(double), Value = CSTypeBuilder.Double },
                new { Key = typeof(decimal), Value = CSTypeBuilder.Decimal },

                new { Key = typeof(bool?), Value = CSTypeBuilder.NullableBool },
                new { Key = typeof(sbyte?), Value = CSTypeBuilder.NullableSByte },
                new { Key = typeof(short?), Value = CSTypeBuilder.NullableShort },
                new { Key = typeof(int?), Value = CSTypeBuilder.NullableInt },
                new { Key = typeof(long?), Value = CSTypeBuilder.NullableLong },
                new { Key = typeof(byte?), Value = CSTypeBuilder.NullableByte },
                new { Key = typeof(ushort?), Value = CSTypeBuilder.NullableUShort },
                new { Key = typeof(uint?), Value = CSTypeBuilder.NullableUInt },
                new { Key = typeof(ulong?), Value = CSTypeBuilder.NullableULong },
                new { Key = typeof(float?), Value = CSTypeBuilder.NullableFloat },
                new { Key = typeof(double?), Value = CSTypeBuilder.NullableDouble },
                new { Key = typeof(decimal?), Value = CSTypeBuilder.NullableDecimal },
            }.ToDictionary(o => o.Key, o => (object)o.Value);
        }
        #endregion

        #region Static Private Data Members
        private static Dictionary<Type, object> s_typeToTObject;
        #endregion

        #region Private Data Members
        private CSSubstitutionType m_type;
        #endregion

        #region Constructor
        internal CSSubstitution(CSSubstitutionType type) {
            m_type = type;
        }
        #endregion

        #region TSubstitute Overrides
        protected override IEnumerable<object> Substitute(TextNodeContext context, object value) {
            if (value is CSVisibility && m_type == CSSubstitutionType.Visibility)
                yield return SubstituteVisibility(context, (CSVisibility)value);

            else if (value is CSAttributeGroupTarget && m_type == CSSubstitutionType.AttributeGroupTarget)
                yield return SubstituteAttributeGroupTarget(context, (CSAttributeGroupTarget)value);

            else if (value is Type && m_type == CSSubstitutionType.Type)
                yield return SubstituteType(context, (Type)value);

            else if (value is Type && m_type == CSSubstitutionType.AttributeType)
                yield return SubstituteAttributeType(context, (Type)value);

            else if (value is Type && m_type == CSSubstitutionType.TypeToIdentifier)
                yield return ((Type)value).Name;

            else if (m_type == CSSubstitutionType.Literal)
                yield return SubstituteLiteral(context, value);

            else
                yield return value;
        }
        #endregion
    }
    #endregion

    #region Literals (CSKeyWord, CSString, CSTypeOf, CSIdentifier, CSEnumValue, CSArray)
    public class CSKeyWord : TextNode {

        #region Static Internal Members
        internal static bool IsKeyWord(string value) {
            return s_keyWords.Contains(value);
        }
        #endregion

        #region Internal Static Data Members
        internal static object Event = new CSKeyWord("event");
        internal static object Field = new CSKeyWord("field");
        internal static object Method = new CSKeyWord("method");
        internal static object Module = new CSKeyWord("module");
        internal static object Param = new CSKeyWord("param");
        internal static object Property = new CSKeyWord("property");
        internal static object Return = new CSKeyWord("return");
        internal static object Type = new CSKeyWord("type");

        internal static object Void = new CSKeyWord("void");
        internal static object Object = new CSTypeRef(typeof(object), new CSKeyWord("object"));
        internal static object Bool = new CSTypeRef(typeof(bool), new CSKeyWord("bool"));
        internal static object String = new CSTypeRef(typeof(string), new CSKeyWord("string"));
        internal static object Char = new CSTypeRef(typeof(char), new CSKeyWord("char"));
        internal static object SByte = new CSTypeRef(typeof(sbyte), new CSKeyWord("sbyte"));
        internal static object Short = new CSTypeRef(typeof(short), new CSKeyWord("short"));
        internal static object Int = new CSTypeRef(typeof(int), new CSKeyWord("int"));
        internal static object Long = new CSTypeRef(typeof(long), new CSKeyWord("long"));
        internal static object Byte = new CSTypeRef(typeof(byte), new CSKeyWord("byte"));
        internal static object UShort = new CSTypeRef(typeof(ushort), new CSKeyWord("ushort"));
        internal static object UInt = new CSTypeRef(typeof(uint), new CSKeyWord("uint"));
        internal static object ULong = new CSTypeRef(typeof(ulong), new CSKeyWord("ulong"));
        internal static object Double = new CSTypeRef(typeof(double), new CSKeyWord("double"));
        internal static object Float = new CSTypeRef(typeof(float), new CSKeyWord("float"));
        internal static object Decimal = new CSTypeRef(typeof(float), new CSKeyWord("float"));

        internal static object Public = new CSKeyWord("public");
        internal static object Private = new CSKeyWord("private");
        internal static object Internal = new CSKeyWord("internal");
        internal static object Protected = new CSKeyWord("protected");

        internal static object Namespace = new CSKeyWord("namespace");
        internal static object Using = new CSKeyWord("using");
        internal static object As = new CSKeyWord("as");
        internal static object Is = new CSKeyWord("is");
        internal static object If = new CSKeyWord("if");
        internal static object Else = new CSKeyWord("else");
        internal static object While = new CSKeyWord("while");
        internal static object Do = new CSKeyWord("do");
        internal static object Switch = new CSKeyWord("switch");
        internal static object Case = new CSKeyWord("case");
        internal static object Default = new CSKeyWord("default");

        internal static object Delegate = new CSKeyWord("delegate");

        internal static object Abstract = new CSKeyWord("abstract");
        internal static object Virtual = new CSKeyWord("virtual");
        internal static object Override = new CSKeyWord("override");
        internal static object Sealed = new CSKeyWord("sealed");
        internal static object Extern = new CSKeyWord("extern");
        internal static object New = new CSKeyWord("new");
        internal static object This = new CSKeyWord("this");
        internal static object Params = new CSKeyWord("params");
        internal static object Get = new CSKeyWord("get");
        internal static object Set = new CSKeyWord("set");
        internal static object Add = new CSKeyWord("add");
        internal static object Remove = new CSKeyWord("remove");

        internal static object Static = new CSKeyWord("static");
        internal static object Partial = new CSKeyWord("partial");
        internal static object Class = new CSKeyWord("class");
        internal static object Interface = new CSKeyWord("interface");
        internal static object Enum = new CSKeyWord("enum");
        internal static object Struct = new CSKeyWord("struct");
        internal static object Base = new CSKeyWord("base");

        internal static object True = new CSKeyWord("true");
        internal static object False = new CSKeyWord("false");
        internal static object Null = new CSKeyWord("null");
        internal static object Typeof = new CSKeyWord("typeof");

        internal static HashSet<string> s_keyWords;
        #endregion

        #region Private Data Members
        private object m_keyWord;
        #endregion

        #region Constructor
        internal CSKeyWord(object keyWord) {
            m_keyWord = keyWord;
        }
        internal CSKeyWord(string keyWord)
            : this((object)keyWord) {

            if (s_keyWords == null)
                s_keyWords = new HashSet<string>();
            s_keyWords.Add(keyWord);
        }
        #endregion

        #region TextNode Override
        protected override IEnumerable<object> Children(TextNodeContext context) {
            yield return m_keyWord;
        }
        #endregion
    }
    public class CSString : TextNode {

        #region Private Data Members
        private object m_value;
        #endregion

        #region Constructor
        public CSString(object value) {
            m_value = value;
        }
        #endregion

        #region TextNode Override
        protected override IEnumerable<object> Content(TextNodeContext context) {
            return new[] { m_value };
        }
        protected override IEnumerable<object> Children(TextNodeContext context) {
            return new object[] { IsVerbatim ? "@\"" : "\"", m_value, "\"" };
        }
        #endregion

        #region Public Members
        public bool IsVerbatim { get; set; }
        #endregion
    }
    public sealed class CSTypeOf : TextNode {

        #region Private Data Members
        private object m_type;
        #endregion

        #region Constructor
        public CSTypeOf(object type) {
            m_type = type;
        }
        #endregion

        #region TextNode Overrides
        protected override IEnumerable<object> Children(TextNodeContext context) {
            return new object[] {
                CSKeyWord.Typeof, "(", new CSSubstitution(CSSubstitutionType.Type) { m_type }, ")"
            };
        }
        #endregion
    }
    public sealed class CSIdentifier : TextNode {

        #region Private Data Members
        private object m_name;
        #endregion

        #region Constructor
        internal CSIdentifier(object name) {
            m_name = name;
        }
        #endregion

        #region TextNode Overrides
        protected override IEnumerable<object> Children(TextNodeContext context) {
            string name = ToIdentifier(context, m_name);
            if (CSKeyWord.IsKeyWord(name))
                name = "@" + name;
            return new[] { name };
        }
        #endregion
    }
    public class CSArray : TextNodes {

        #region Private Data Members
        private object m_arrayType;
        #endregion

        #region Constructor
        public CSArray() {
        }
        public CSArray(object arrayType) {
            m_arrayType = arrayType;
        }
        #endregion

        #region TextNode Overrides
        protected sealed override IEnumerable<object> Content(TextNodeContext context) {
            return ArbitraryContent;
        }
        protected sealed override IEnumerable<object> Children(TextNodeContext context) {

            var arrayType = new TextJoin(prefix: " ") { new CSSubstitution(CSSubstitutionType.Type) { m_arrayType } };

            object elements;
            var content = context.Flatten(Children());
            if (!content.Any())
                elements = "{ }";
            else if (!content.Skip(1).Any())
                elements = new object[] { "{ ", content.Single(), " }" };
            else
                elements = new object[] { "{", Environment.NewLine,
                    new CSIndent() {
                        new TextJoin("," + Environment.NewLine) { content }, 
                    }, Environment.NewLine,
                    "}" 
                };

            return new object[] { CSKeyWord.New, arrayType, "[] ", elements };
        }
        #endregion
    }
    #endregion

    #region Type (CSTypeName, CSTypeRef, CSTypeBuilder)
    public sealed class CSTypeName : TextNode {

        #region Private Data Members
        private object m_name;
        #endregion

        #region Constructor
        internal CSTypeName(object name) {
            m_name = name;
        }
        #endregion

        #region TextNode Overrides
        protected override IEnumerable<object> Children(TextNodeContext context) {
            return new[] { new CSSubstitution(CSSubstitutionType.TypeToIdentifier) { m_name } };
        }
        #endregion
    }
    public sealed class CSTypeRef : TextNode {

        #region Private Data Members
        private Type m_type;
        private object m_value;
        #endregion

        #region Constructor
        internal CSTypeRef(Type type, object value) {
            if (type == null)
                throw new ArgumentNullException();

            m_type = type;
            m_value = value;
        }
        #endregion

        #region Public Members
        public Type Type { get { return m_type; } }
        #endregion

        #region TextNode Overrides
        protected override IEnumerable<object> Children(TextNodeContext context) {
            return new[] { new CSSubstitution(CSSubstitutionType.Type) { m_value } };
        }
        #endregion
    }
    public sealed class CSTypeBuilder : TextNode {

        #region Public Static Members
        public static object Void = new CSTypeBuilder() { Name = new CSKeyWord("void") };
        public static object Object = new CSTypeBuilder() { Name = new CSKeyWord("object") };
        public static object String = new CSTypeBuilder() { Name = new CSKeyWord("string") };

        public static object Bool = new CSTypeBuilder() { Name = new CSKeyWord("bool") };
        public static object Char = new CSTypeBuilder() { Name = new CSKeyWord("char") };

        public static object SByte = new CSTypeBuilder() { Name = new CSKeyWord("sbyte") };
        public static object Short = new CSTypeBuilder() { Name = new CSKeyWord("short") };
        public static object Int = new CSTypeBuilder() { Name = new CSKeyWord("int") };
        public static object Long = new CSTypeBuilder() { Name = new CSKeyWord("long") };

        public static object Byte = new CSTypeBuilder() { Name = new CSKeyWord("byte") };
        public static object UShort = new CSTypeBuilder() { Name = new CSKeyWord("ushort") };
        public static object UInt = new CSTypeBuilder() { Name = new CSKeyWord("uint") };
        public static object ULong = new CSTypeBuilder() { Name = new CSKeyWord("ulong") };

        public static object Double = new CSTypeBuilder() { Name = new CSKeyWord("double") };
        public static object Float = new CSTypeBuilder() { Name = new CSKeyWord("float") };
        public static object Decimal = new CSTypeBuilder() { Name = new CSKeyWord("decimal") };

        public static object NullableBool = new CSTypeBuilder() { Name = new CSKeyWord("bool"), IsNullable = true };
        public static object NullableChar = new CSTypeBuilder() { Name = new CSKeyWord("char"), IsNullable = true };

        public static object NullableSByte = new CSTypeBuilder() { Name = new CSKeyWord("sbyte"), IsNullable = true };
        public static object NullableShort = new CSTypeBuilder() { Name = new CSKeyWord("short"), IsNullable = true };
        public static object NullableInt = new CSTypeBuilder() { Name = new CSKeyWord("int"), IsNullable = true };
        public static object NullableLong = new CSTypeBuilder() { Name = new CSKeyWord("long"), IsNullable = true };

        public static object NullableByte = new CSTypeBuilder() { Name = new CSKeyWord("byte"), IsNullable = true };
        public static object NullableUShort = new CSTypeBuilder() { Name = new CSKeyWord("ushort"), IsNullable = true };
        public static object NullableUInt = new CSTypeBuilder() { Name = new CSKeyWord("uint"), IsNullable = true };
        public static object NullableULong = new CSTypeBuilder() { Name = new CSKeyWord("ulong"), IsNullable = true };

        public static object NullableDouble = new CSTypeBuilder() { Name = new CSKeyWord("double"), IsNullable = true };
        public static object NullableFloat = new CSTypeBuilder() { Name = new CSKeyWord("float"), IsNullable = true };
        public static object NullableDecimal = new CSTypeBuilder() { Name = new CSKeyWord("decimal"), IsNullable = true };
        #endregion

        #region Constructor
        public CSTypeBuilder() { }
        public CSTypeBuilder(object name) {
            Name = name;
        }
        public CSTypeBuilder(object name, params object[] genericArguments) {
            Name = name;
            GenericArguments = genericArguments;
        }
        #endregion

        #region TextNode Overrides
        protected override IEnumerable<object> Content(TextNodeContext context) {
            return new[] { new CSSubstitution(CSSubstitutionType.Type) { Name } };
        }
        protected override IEnumerable<object> Children(TextNodeContext context) {
            return new object[] {
                new CSSubstitution(CSSubstitutionType.Type) { Name },
                new TextJoin(prefix: "<", suffix: ">") {
                    new CSSubstitution(CSSubstitutionType.Type) { 
                        new TextJoin(", ") { 
                            from o in context.Flatten(GenericArguments)
                            select new CSSubstitution(CSSubstitutionType.Type) { o }
                        } 
                    },
                },
                IsArray ? "[]" : null,
                IsNullable ? "?" : null
            };
        }
        #endregion

        #region Public Members
        public object Name { get; set; }
        public object GenericArguments { get; set; }
        public bool IsNullable { get; set; }
        public bool IsArray { get; set; }
        #endregion
    }
    #endregion

    #region Code Containers (CSNamespace, CSUsing, CSUsingAs, CSPreProcessorDirective, CSRegion, CSBlock, CSIndent)
    public sealed class CSNamespaceRef : TextNodes {

        #region Private Data Members
        private object m_namespace;
        #endregion

        #region Constructor
        public CSNamespaceRef(object @namespace) {
            m_namespace = @namespace;
        }
        #endregion

        #region Public Data Members
        public bool EmptyIfNoContent { get; set; }
        public string Namespace {
            get { return TextGenerator.ReadToEnd(m_namespace); }
        }
        #endregion
    }
    public sealed class CSNamespace : TextNodes {

        #region Private Data Members
        private object m_namespace;
        #endregion

        #region Constructor
        public CSNamespace(object @namespace) {
            m_namespace = @namespace;
        }
        #endregion

        #region TextNode Overrides
        protected override IEnumerable<object> Content(TextNodeContext context) {
            return new[] { "{ }" };
        }
        protected override IEnumerable<object> Children(TextNodeContext context) {

            var preamble = new object[] { 
                CSKeyWord.Namespace, " ", 
                new CSNamespaceRef(m_namespace) { m_namespace }, 
                " ", 
            };

            if (!context.ContainsContent(Children()))
                return new object[] { preamble, "{ }" };

            var content = new CSBlock() { Children() };

            return new object[] { preamble, content };
        }
        #endregion

        #region Public Data Members
        public bool EmptyIfNoContent { get; set; }
        #endregion

        #region Object Overrides
        public override string ToString() {
            if (m_namespace is string)
                return "CSNamespace: " + (string)m_namespace;
            return base.ToString();
        }
        #endregion
    }
    public sealed class CSPreProcessorDirective : TextNode {

        #region Internal Static Data Members
        internal static CSPreProcessorDirective Region = new CSPreProcessorDirective("#region");
        internal static CSPreProcessorDirective EndRegion = new CSPreProcessorDirective("#endregion");
        internal static CSPreProcessorDirective Error = new CSPreProcessorDirective("#error");
        internal static CSPreProcessorDirective Warning = new CSPreProcessorDirective("#warning");
        internal static CSPreProcessorDirective Define = new CSPreProcessorDirective("#define");
        internal static CSPreProcessorDirective If = new CSPreProcessorDirective("#if");
        internal static CSPreProcessorDirective Else = new CSPreProcessorDirective("#else");
        internal static CSPreProcessorDirective ElseIf = new CSPreProcessorDirective("#elif");
        internal static CSPreProcessorDirective EndIf = new CSPreProcessorDirective("#endif");
        #endregion

        #region Private Data Members
        private object m_keyWord;
        #endregion

        #region Constructor
        internal CSPreProcessorDirective(object keyWord) {
            m_keyWord = keyWord;
        }
        #endregion

        #region TextNode Override
        protected override IEnumerable<object> Children(TextNodeContext context) {
            yield return m_keyWord;
        }
        #endregion
    }
    public sealed class CSUsingAs : TextNode {

        #region Private Data Members
        private object m_alias;
        private object m_ns;
        #endregion

        #region Constructor
        public CSUsingAs(object alias, object ns) {
            m_alias = alias;
            m_ns = ns;
        }
        #endregion

        #region TextNode Overrides
        protected override IEnumerable<object> Content(TextNodeContext context) {
            return ArbitraryContent;
        }
        protected override IEnumerable<object> Children(TextNodeContext context) {
            return new[] { CSKeyWord.Using, " ", m_alias, " = ", m_ns, ";" };
        }
        #endregion
    }
    public sealed class CSUsingNamespace : TextNodes {

        #region TextNode Overrides
        protected override IEnumerable<object> Children(TextNodeContext context) {
            return new TextSingleSpaced() {
                Children().Select(o => new TextNodes() { CSKeyWord.Using, " ", o, ";" })
            };
        }
        #endregion
    }
    public sealed class CSInlineBlock : CSBlock {

        #region Constructor
        public CSInlineBlock() {
            InLineContent = true;
        }
        #endregion
    }
    public class CSBlock : TextNodes {

        #region TextNode Overrides
        protected override IEnumerable<object> Content(TextNodeContext context) {
            return Children();
        }
        protected override IEnumerable<object> Children(TextNodeContext context) {
            var newLine = Environment.NewLine;

            if (!context.ContainsContent(Children()))
                return new[] { "{", Environment.NewLine, "}" };

            if (InLineContent && !context.ContainsNewLine(Children()))
                newLine = " ";

            return new object[] {
                "{", newLine, 
                new CSIndent() { Children() },
                newLine, "}"
            };
        }
        #endregion

        #region Internal Members
        internal bool InLineContent { get; set; }
        #endregion
    }
    public sealed class CSRegion : TextNodes {

        #region Private Data Members
        private string m_name;
        #endregion

        #region Constructor
        public CSRegion(string name) {
            m_name = name;
        }
        #endregion

        #region TextNode Overrides
        protected override IEnumerable<object> Children(TextNodeContext context) {
            yield return new TextJoin(
                    prefix: new object[] { CSPreProcessorDirective.Region, " ", m_name, Environment.NewLine },
                    suffix:new object[] { Environment.NewLine, CSPreProcessorDirective.EndRegion }
                ) {
                    new TextSingleSpaced() { Children() },
                };
        }
        #endregion

        #region Object Overrides
        public override string ToString() {
            if (m_name is string)
                return "CSRegion: " + (string)m_name;
            return base.ToString();
        }
        #endregion
    }
    public sealed class CSCommentRegion : TextNodes {

        #region Private Data Members
        private string m_comment;
        #endregion

        #region Constructor
        public CSCommentRegion(string comment) {
            m_comment = comment;
        }
        #endregion

        #region TextNode Overrides
        protected override IEnumerable<object> Children(TextNodeContext context) {
            yield return new TextJoin(prefix: new object[] { "//", m_comment, Environment.NewLine }) {
                new TextSingleSpaced() { Children() }
            };
        }
        #endregion
    }
    public class CSIndent : TextNodes {

        #region Static Public Data Members
        public new static readonly object DefaultIndent = "    ";
        #endregion

        private object m_value;

        #region Constructor
        public CSIndent() {
        }
        public CSIndent(object value) {
            m_value = value;
        }
        #endregion

        protected override IEnumerable<object> Children(TextNodeContext context) {
            return new TextIndent(m_value) { Children() };
        }
    }
    #endregion

    #region DocComments (CSXAttribute, CSXElement, CSDocSummary, CSDocParameter, CSDocReturns)
    public class CSDocComment : TextNodes {

        #region Public Static Members
        public static readonly object TrippleSlash = new CSDocComment() { "/// " };
        #endregion
    }
    public sealed class CSXElementName : TextNodes {
    }
    public sealed class CSXAttributeName : TextNodes {
    }
    public sealed class CSXText : TextNodes {
    }
    public class CSXAttribute : TextNode {

        #region Private Data Members
        private object m_name;
        private object m_value;
        #endregion

        #region Constructor
        public CSXAttribute(object name, object value) {
            m_name = name;
            m_value = value;
        }
        #endregion

        #region TextNode Overrides
        protected override IEnumerable<object> Content(TextNodeContext context) {
            return new[] { m_value };
        }
        protected override IEnumerable<object> Children(TextNodeContext context) {
            return new[] {
                new TXAttribute(CSXElement.EscapeContext) {
                    Name = new CSXAttributeName() { m_name },
                    Value = m_value
                }
            };
        }
        #endregion
    }
    public abstract class CSXElement : TextNodes {

        #region Static Public Members
        public static object EscapeContext = new object();
        #endregion

        #region Private Data Members
        private object m_name;
        private object m_attributes;
        #endregion

        #region Constructor
        public CSXElement(object name)
            : this(name, null) {
        }
        public CSXElement(object name, object attributes) {
            m_name = name;
            m_attributes = attributes;
        }
        #endregion

        #region TextNode Overrides
        protected override IEnumerable<object> Content(TextNodeContext context) {
            return base.Children();
        }
        protected override IEnumerable<object> Children(TextNodeContext context) {
            var newLine = Environment.NewLine;

            if (!context.ContainsNewLine(Children()))
                newLine = null;

            var content = new object[] { newLine, Children(), newLine };

            return new[] { 
                new TXElement(EscapeContext) {
                    Name = new CSXElementName() { m_name },
                    Content = new CSXText() { content },
                    Attributes = m_attributes,
                    InlineContent = true,
                }
            };
        }
        #endregion
    }
    public sealed class CSDocSummary : CSXElement {

        #region Constructor
        public CSDocSummary()
            : base("summary") {
        }
        #endregion
    }
    public sealed class CSDocParameter : CSXElement {

        #region Constructor
        public CSDocParameter(object name)
            : base("param", new CSXAttribute("name", name)) {
        }
        #endregion
    }
    public sealed class CSDocReturns : CSXElement {

        #region Constructor
        public CSDocReturns()
            : base("returns") {
        }
        #endregion
    }
    #endregion

    #region Definitions (CSVisibility, CSMember, CSClass, CSField, CSMethod, CSConstructor, CSParameter)
    public enum CSVisibility {
        None,
        Public,
        Private,
        Internal,
        Protected,
        ProtectedInternal,
    }
    public sealed class CSPadding : TextNodes {

        #region Internal Static Members
        internal static string GetPadding(TextNodeContext context, int length) {
            var padding = string.Empty;
            var csPadding = context.GetValue<CSPadding>();
            if (csPadding != null && csPadding.Padding > 0)
                padding = string.Empty.PadLeft(csPadding.Padding - length);

            return padding;
        }
        #endregion

        #region Private Data Members
        private int m_padding;
        #endregion

        #region Constructor
        public CSPadding(int padding) {
            m_padding = padding;
        }
        #endregion

        #region Public Members
        public int Padding {
            get { return m_padding; }
        }
        #endregion
    }
    public abstract class CSMember : TextNode {

        #region Constructor
        internal CSMember() { }
        #endregion

        #region Public Members
        public object Documentation { get; set; }
        public object Attributes { get; set; }
        public object Name { get; set; }
        #endregion
    }
    public sealed class CSInterface : CSMember {

        #region Private Members
        private IEnumerable<object> Modifiers() {
            yield return new CSSubstitution(CSSubstitutionType.Visibility) { Visibility };

            if (IsPartial)
                yield return CSKeyWord.Partial;
        }
        #endregion

        #region TextNode Overrides
        protected override IEnumerable<object> Children(TextNodeContext context) {
            return new object[] { 

                // documentation
                new CSIndent(CSDocComment.TrippleSlash) {
                    new TextLine() { new CSDocSummary() { Documentation } },
                },

                // attributes
                new TextLine() { CSAttributeGroup.GroupAttributes(context, Attributes) },
                
                // modifiers
                new TextJoin(" ") {
                    Modifiers(), 
                    CSKeyWord.Interface,
                    new CSTypeName(Name),
                },

                new TextJoin(prefix: " : ") {
                    new TextJoin(", ") { Interfaces },                    
                }, " ",

                new CSBlock() { Members } 
            };
        }
        #endregion

        #region Public Data Members
        public CSVisibility Visibility { get; set; }
        public bool IsPartial { get; set; }
        public object Interfaces { get; set; }
        public object Members { get; set; }
        #endregion
    }
    public sealed class CSStruct : CSMember {

        #region Private Members
        private IEnumerable<object> Modifiers() {
            yield return new CSSubstitution(CSSubstitutionType.Visibility) { Visibility };

            if (IsExtern)
                yield return CSKeyWord.Extern;

            if (IsPartial)
                yield return CSKeyWord.Partial;
        }
        #endregion

        #region TextNode Overrides
        protected override IEnumerable<object> Children(TextNodeContext context) {
            if (EmptyIfNoMembers && !context.ContainsContent(Members))
                return null;

            return new object[] { 

                // documentation
                new CSIndent(CSDocComment.TrippleSlash) {
                    new TextLine() { new CSDocSummary() { Documentation } },
                },

                // attributes
                new TextLine() { CSAttributeGroup.GroupAttributes(context, Attributes) },
                
                // modifiers
                new TextJoin(" ") {
                    Modifiers(), 
                    CSKeyWord.Struct,
                    new CSTypeName(Name),
                },

                new TextJoin(prefix: " : ") {
                    new TextJoin(", ") { Interfaces },                    
                }, " ",

                new CSBlock() { Members } 
            };
        }
        #endregion

        #region Public Data Members
        public CSVisibility Visibility { get; set; }
        public bool IsExtern { get; set; }
        public bool IsPartial { get; set; }
        public bool EmptyIfNoMembers { get; set; }

        public object Interfaces { get; set; }

        public object Members { get; set; }
        #endregion
    }
    public sealed class CSClass : CSMember {

        #region Private Members
        private IEnumerable<object> Modifiers() {
            yield return new CSSubstitution(CSSubstitutionType.Visibility) { Visibility };

            if (IsStatic)
                yield return CSKeyWord.Static;

            if (IsExtern)
                yield return CSKeyWord.Extern;

            if (IsAbstract)
                yield return CSKeyWord.Abstract;

            if (IsSealed)
                yield return CSKeyWord.Sealed;

            if (IsPartial)
                yield return CSKeyWord.Partial;
        }
        #endregion

        #region TextNode Overrides
        protected override IEnumerable<object> Children(TextNodeContext context) {
            if (EmptyIfNoMembers && context.ContainsContent(Members))
                return null;

            return new object[] { 

                // documentation
                new CSIndent(CSDocComment.TrippleSlash) {
                    new TextLine() { new CSDocSummary() { Documentation } },
                },

                // attributes
                new TextLine() { CSAttributeGroup.GroupAttributes(context, Attributes) },
                
                // modifiers
                new TextJoin(" ") {
                    Modifiers(), 
                    CSKeyWord.Class,
                    new CSTypeName(Name),
                },

                new TextJoin(prefix:  " : ") {
                    new CSSubstitution(CSSubstitutionType.Type) {
                        new TextJoin(", ") { BaseType, Interfaces },
                    }
                }, " ",

                new CSBlock() { Members } 
            };
        }
        #endregion

        #region Public Data Members
        public CSVisibility Visibility { get; set; }
        public bool IsStatic { get; set; }
        public bool IsExtern { get; set; }
        public bool IsAbstract { get; set; }
        public bool IsPartial { get; set; }
        public bool IsSealed { get; set; }
        public bool EmptyIfNoMembers { get; set; }

        public object BaseType { get; set; }
        public object Interfaces { get; set; }

        public object Members { get; set; }
        #endregion
    }
    public sealed class CSField : CSMember {

        #region Private Members
        private IEnumerable<object> Modifiers() {
            yield return new CSSubstitution(CSSubstitutionType.Visibility) { Visibility };

            if (IsStatic)
                yield return CSKeyWord.Static;

            if (IsExtern)
                yield return CSKeyWord.Extern;

            if (IsNew)
                yield return CSKeyWord.New;
        }
        #endregion

        #region TextNode Override
        protected override IEnumerable<object> Children(TextNodeContext context) {
            var identifier = ToIdentifier(context, new CSIdentifier(Name));

            return new object[] { 

                // documentation
                new CSIndent(CSDocComment.TrippleSlash) {
                    new TextLine() { new CSDocSummary() { Documentation } },
                },

                // attributes
                new TextLine() { CSAttributeGroup.GroupAttributes(context, Attributes) },
                
                // modifiers
                new TextJoin(" ") {
                    Modifiers(), 
                    new CSSubstitution(CSSubstitutionType.Type) { Type },
                    new CSIdentifier(Name),
                },

                new TextJoin(prefix: CSPadding.GetPadding(context, identifier.Length) + " = ") {
                    new CSSubstitution(CSSubstitutionType.Literal) { 
                        Initializer 
                    }
                },

                ";"
            };
        }
        #endregion

        #region Public Members
        public CSVisibility Visibility { get; set; }
        public bool IsStatic { get; set; }
        public bool IsExtern { get; set; }
        public bool IsNew { get; set; }
        public object Type { get; set; }
        public object Initializer { get; set; }
        #endregion
    }
    public sealed class CSConstructor : CSMember {

        #region Private Members
        private IEnumerable<object> Modifiers() {
            yield return new CSSubstitution(CSSubstitutionType.Visibility) { Visibility };

            if (IsStatic)
                yield return CSKeyWord.Static;

            if (IsExtern)
                yield return CSKeyWord.Extern;
        }
        #endregion

        #region Protected Members
        protected override IEnumerable<object> Content(TextNodeContext context) {
            return ArbitraryContent;
        }
        protected override IEnumerable<object> Children(TextNodeContext context) {

            // parameters
            var parameterCapture = new TCapture<CSParameter>() { new TextJoin(", ") { Parameters } };
            var parameters = new object[] { "(", (object)context.Evaluate(parameterCapture), ")" };

            // parameter documentation
            var docParams = parameterCapture.Captures
                .Select(o => new TextLine() { 
                    new CSDocParameter(new CSIdentifier(o.Name)) { 
                        o.Documentation 
                    }
                });

            yield return new object[] { 
                // documentation
                new CSIndent(CSDocComment.TrippleSlash) {
                    new TextLine() { new CSDocSummary() { Documentation } },
                    docParams,
                },

                // attributes
                new TextLine() { CSAttributeGroup.GroupAttributes(context, Attributes) },

                // modifiers, return, name
                new TextJoin(" ") {
                    Modifiers(), 
                    new CSIdentifier(Name),
                },
                
                // parameters
                parameters,
            };

            yield return new TextJoin(
                prefix: new object[] { Environment.NewLine, ": ", CSKeyWord.Base, "(" },
                suffix: ") ") { BaseConstructor };

            if (IsExtern) {
                yield return ";";
            } else {
                yield return " ";
                yield return new CSBlock() { Body };
            }
        }
        #endregion

        #region Public Members
        public CSVisibility Visibility { get; set; }
        public bool IsStatic { get; set; }
        public object Parameters { get; set; }
        public object Returns { get; set; }
        public object Body { get; set; }
        public object BaseConstructor { get; set; }
        public bool IsExtension { get; set; }

        public object DocumentationParams { get; set; }
        public object DocumentationReturns { get; set; }

        public bool IsExtern { get; set; }
        #endregion
    }
    public sealed class CSMethod : CSMember {

        #region Private Members
        private IEnumerable<object> Modifiers() {
            yield return new CSSubstitution(CSSubstitutionType.Visibility) { Visibility };

            if (IsStatic)
                yield return CSKeyWord.Static;

            if (IsExtern)
                yield return CSKeyWord.Extern;

            if (IsNew)
                yield return CSKeyWord.New;

            if (IsAbstract)
                yield return CSKeyWord.Abstract;

            if (IsVirtual)
                yield return CSKeyWord.Virtual;

            if (IsSealed)
                yield return CSKeyWord.Sealed;

            if (IsOverride)
                yield return CSKeyWord.Override;

            if (IsPartial)
                yield return CSKeyWord.Partial;
        }
        #endregion

        #region Protected Members
        protected override IEnumerable<object> Content(TextNodeContext context) {
            return ArbitraryContent;
        }
        protected override IEnumerable<object> Children(TextNodeContext context) {

            // parameters
            IsExtensionParameter = IsExtension;
            var parameterCapture = new TCapture<CSParameter>() { new TextJoin(", ") { Parameters } };
            var parameters = new object[] { "(", (object)context.Evaluate(parameterCapture), ")" };

            // return parameter
            var returns = Returns ?? typeof(void);
            var returnParameterCapture = new TCapture<CSParameter>() { new CSSubstitution(CSSubstitutionType.Type) { returns } };
            var returnParameter = context.Evaluate(returnParameterCapture);

            // parameter documentation
            var docParams = parameterCapture.Captures
                .Select(o => new TextLine() { 
                    new CSDocParameter(new CSIdentifier(o.Name)) { 
                        o.Documentation 
                    }
                });

            // return parameter attributes & documentation
            var returnAttributes = returnParameterCapture.Captures.Select(o => o.Attributes).SingleOrDefault();
            var returnDocs = returnParameterCapture.Captures.Select(o => o.Documentation).SingleOrDefault();

            // generic method parameters
            object genericArguments = new CSSubstitution(CSSubstitutionType.Type) { new TextJoin(", ") { GenericArguments } };
            if (context.ContainsContent(genericArguments))
                genericArguments = new object[] { "<", genericArguments, ">" };

            yield return new object[] { 
                // documentation
                new CSIndent(CSDocComment.TrippleSlash) {
                    new TextLine() { new CSDocSummary() { Documentation } },
                    docParams,
                    new TextLine() { new CSDocReturns() { returnDocs } },
                },

                // attributes
                new TextLine() { CSAttributeGroup.GroupAttributes(context, Attributes) },
                new TextLine() { CSAttributeGroup.GroupAttributes(context, CSAttributeGroupTarget.Return, returnAttributes) },

                // modifiers, return, name
                new TextJoin(" ") {
                    Modifiers(), 
                    returnParameter,
                    new CSIdentifier(Name),
                },
                
                // generic arguments
                genericArguments,

                // parameters
                parameters,
            };

            if (IsExtern || IsAbstract) {
                yield return ";";
            } else {
                yield return " ";
                yield return new CSBlock() { Body };
            }
        }
        #endregion

        #region Internal Members
        internal bool IsExtensionParameter;
        #endregion

        #region Public Members
        public CSVisibility Visibility { get; set; }
        public bool IsStatic { get; set; }
        public object Parameters { get; set; }
        public object Returns { get; set; }
        public object GenericArguments { get; set; }
        public object Body { get; set; }
        public bool IsExtension { get; set; }

        public object DocumentationParams { get; set; }
        public object DocumentationReturns { get; set; }

        public bool IsExtern { get; set; }
        public bool IsPartial { get; set; }
        public bool IsNew { get; set; }
        public bool IsAbstract { get; set; }
        public bool IsVirtual { get; set; }
        public bool IsSealed { get; set; }
        public bool IsOverride { get; set; }
        #endregion
    }
    public sealed class CSParameter : TextNode {

        #region Constructor
        public CSParameter() { }
        public CSParameter(object type)
            : this(type, null) {
        }
        public CSParameter(object type, object name) {
            Type = type;
            Name = name;
        }
        #endregion

        #region TextNode Overrides
        protected override IEnumerable<object> Content(TextNodeContext context) {
            return ArbitraryContent;
        }
        protected override IEnumerable<object> Children(TextNodeContext context) {
            var type = new CSSubstitution(CSSubstitutionType.Type) { Type };

            if (Name == null)
                return new[] { type };

            var isExtension = IsExtension;
            var method = context.GetValue<CSMethod>();
            if (method != null) {
                if (!isExtension)
                    isExtension = method.IsExtensionParameter;
                method.IsExtensionParameter = false;
            }

            return new object[] {
                CSAttributeGroup.GroupAttributes(context, Attributes),
                new TextJoin(" ") {
                    isExtension ? CSKeyWord.This : null,
                    IsParams ? CSKeyWord.Params : null,
                    type, 
                    new CSIdentifier(Name) 
                }
            };
        }
        #endregion

        #region Public Members
        public bool IsParams { get; set; }
        public bool IsExtension { get; set; }
        public object Documentation { get; set; }
        public object Attributes { get; set; }
        public object Type { get; set; }
        public object Name { get; set; }
        #endregion
    }
    #endregion

    #region Definitions (CSEnum, CSEnumField, CSEnumValue)
    public sealed class CSEnum : CSMember {

        #region TextNode Overrides
        protected override IEnumerable<object> Children(TextNodeContext context) {
            return new object[] { 

                // documentation
                new CSIndent(CSDocComment.TrippleSlash) {
                    new TextLine() { new CSDocSummary() { Documentation } },
                },

                // attributes
                new TextLine() { CSAttributeGroup.GroupAttributes(context, Attributes) },
                
                // modifiers
                new TextJoin(" ") {
                    new CSSubstitution(CSSubstitutionType.Visibility) { Visibility },
                    CSKeyWord.Enum,
                    new CSTypeName(Name),
                },

                // optional baseType
                new TextJoin(prefix: " : ") { new CSSubstitution(CSSubstitutionType.Type) { BaseType } },
                
                " ",

                new CSBlock() { Members } 
            };
        }
        #endregion

        #region Public Data Members
        public CSVisibility Visibility { get; set; }
        public object BaseType { get; set; }
        public object Members { get; set; }
        #endregion
    }
    public sealed class CSEnumField : CSMember {

        #region Static Public Members
        public static string SubstituteBitMask(long value) {
            if (value == 0 || value == -1)
                return value.ToString();

            return value.GetBits().Select(o => (o >= 31 ? "1L" : "1") + " << " + o).StringJoin(" | ");
        }
        public static string SubstituteEnumValue(EnumValue value) {
            var longValue = value.LongValue;
            if (!value.IsFlags || value.LongValue == -1)
                return longValue.ToString();

            var namedValues = value.Values().GreatestCommonSets((l, r) => l.Contains(r)).ToArray();
            var unnamedValues = longValue & ~namedValues.Aggregate(0L, (a, x) => a |= x.LongValue);

            var namedValueNames = namedValues.Select(o => o.Name);
            var unnamedValueBitShifts = unnamedValues.ToCSharpBitMask();

            var resultNames = namedValueNames;
            if (resultNames.None() || unnamedValues != 0)
                resultNames = resultNames.Union(unnamedValueBitShifts);

            var result = resultNames.StringJoin(" | ");
            return result;
        }
        public static string SubstituteEnumMask(MaskValue mask) {
            var result = string.Join(" | ", mask.Values().Select(x => x.Name).ToArray());
            return result;
        }
        #endregion

        #region TextNode Override
        protected override IEnumerable<object> Children(TextNodeContext context) {
            var identifier = ToIdentifier(context, Name);

            return new object[] { 

                // documentation
                new CSIndent(CSDocComment.TrippleSlash) {
                    new TextLine() { new CSDocSummary() { Documentation } },
                },

                // attributes
                new TextLine() { CSAttributeGroup.GroupAttributes(context, Attributes) },

                // name
                new CSIdentifier(Name),

                // initializer
                new TextJoin(prefix: CSPadding.GetPadding(context, identifier.Length) + " = ") { 
                    new CSSubstitution(CSSubstitutionType.Literal) { 
                        Initializer 
                    }
                },

                ","
            };
        }
        #endregion

        #region Public Members
        public object Initializer { get; set; }
        #endregion
    }
    public class CSEnumValue : TextNode {

        #region Private Data Members
        private object m_enum;
        private object m_value;
        #endregion

        #region Constructor
        public CSEnumValue(object @enum, object value) {
            m_enum = @enum;
            m_value = value;
        }
        #endregion

        #region TextNode Overrides
        protected override IEnumerable<object> Children(TextNodeContext context) {
            return new object[] { 
                new CSSubstitution(CSSubstitutionType.Type) { m_enum }, 
                ".", 
                new CSIdentifier(m_value) 
            };
        }
        #endregion
    }
    #endregion

    #region Definitions (CSProperty, CSAccessor, CSAccessorGet, CSAccessorSet)
    public sealed class CSProperty : CSMember {

        #region Private Members
        private IEnumerable<object> Modifiers() {
            yield return new CSSubstitution(CSSubstitutionType.Visibility) { Visibility };

            if (IsStatic)
                yield return CSKeyWord.Static;

            if (IsExtern)
                yield return CSKeyWord.Extern;

            if (IsNew)
                yield return CSKeyWord.New;

            if (IsAbstract)
                yield return CSKeyWord.Abstract;

            if (IsVirtual)
                yield return CSKeyWord.Virtual;

            if (IsSealed)
                yield return CSKeyWord.Sealed;

            if (IsOverride)
                yield return CSKeyWord.Override;
        }
        #endregion

        #region TextNode Override
        protected override IEnumerable<object> Children(TextNodeContext context) {

            // indexer
            var parameterCapture = new TCapture<CSParameter>() { new TextJoin(", ") { Parameters } };
            var parameters = new TCondition("[", context.Evaluate(parameterCapture), "]");

            // indexer documentation
            var docParams = parameterCapture.Captures.Select(o =>
                new TextLine() { new CSDocParameter(new CSIdentifier(o.Name)) { o.Documentation } 
            });

            yield return new object[] { 
                // documentation
                new CSIndent(CSDocComment.TrippleSlash) {
                    new TextLine() { new CSDocSummary() { Documentation } },
                    docParams,
                },

                // attributes
                new TextLine() { CSAttributeGroup.GroupAttributes(context, Attributes) },

                // modifiers, return, name
                new TextJoin(" ") {
                    Modifiers(), 
                    new CSSubstitution(CSSubstitutionType.Type) { Type },
                    new CSIdentifier(Name),
                },
                
                // parameters
                parameters, 
                
                " "
            };

            if (GetAccessor == null && GetterBody != null)
                GetAccessor = new CSAccessorGet() { Body = GetterBody };

            if (SetAccessor == null && SetterBody != null)
                SetAccessor = new CSAccessorSet() { Body = SetterBody };

            if (GetAccessor != null || SetAccessor != null) {

                //var getCapture = new TCapture<CSAccessor>() { GetAccessor };
                //var getAccessor = context.Evaluate(getCapture);

                //var setCapture = new TCapture<CSAccessor>() { SetAccessor };
                //var setAccessor = context.Evaluate(setCapture);

                //var inline = getCapture.Captures.Union(setCapture.Captures).All(o => o.Body == null);

                var block = new CSBlock() {
                    //inline ? (object)
                    //    new TextJoin(" ") { getAccessor, setAccessor } : 
                        new TextSingleSpaced() {
                            //getAccessor
                            GetAccessor,
                            //setAccessor
                            SetAccessor
                        }
                };
                //block.InLineContent = inline;
                yield return block;

            } else {
                if (IsReadable == null && IsWriteable == null)
                    IsReadable = IsWriteable = true;

                yield return new object[] { 
                    "{ ", 
                    IsReadable == true ? new object[] { CSKeyWord.Get, "; " } : null, 
                    IsWriteable == true ? new object[] { CSKeyWord.Set, "; " } : null, 
                    "}" };
            }
        }
        #endregion

        #region Public Members
        public CSVisibility Visibility { get; set; }
        public bool IsStatic { get; set; }
        public object Parameters { get; set; }

        public bool? IsReadable { get; set; }
        public bool? IsWriteable { get; set; }
        public object GetAccessor { get; set; }
        public object SetAccessor { get; set; }
        public object GetterBody { get; set; }
        public object SetterBody { get; set; }

        public object Type { get; set; }
        public bool IsExtern { get; set; }
        public bool IsPartial { get; set; }
        public bool IsNew { get; set; }
        public bool IsAbstract { get; set; }
        public bool IsVirtual { get; set; }
        public bool IsSealed { get; set; }
        public bool IsOverride { get; set; }
        #endregion
    }
    public abstract class CSAccessor : TextNode {

        #region Private Data Members
        private object m_accessorType;
        #endregion

        #region Constructor
        internal CSAccessor(object accessorType) {
            m_accessorType = accessorType;
        }
        #endregion

        #region Protected Members
        protected sealed override IEnumerable<object> Content(TextNodeContext context) {
            return ArbitraryContent;
        }
        protected sealed override IEnumerable<object> Children(TextNodeContext context) {
            return new object[] { 

                // attributes
                new TextLine() { CSAttributeGroup.GroupAttributes(context, Attributes) },

                // modifiers, return, name
                new TextJoin(" ") {
                    new CSSubstitution(CSSubstitutionType.Visibility) { Visibility },
                    m_accessorType,
                },
                Body == null ? (object)";" : new object[] { " ", new CSInlineBlock() { Body } }
            };
        }
        #endregion

        #region Public Members
        public object Attributes { get; set; }
        public CSVisibility Visibility { get; set; }
        public object Body { get; set; }
        #endregion
    }
    public sealed class CSAccessorGet : CSAccessor {

        #region Constructor
        public CSAccessorGet()
            : base(CSKeyWord.Get) {
        }
        #endregion
    }
    public sealed class CSAccessorSet : CSAccessor {

        #region Constructor
        public CSAccessorSet()
            : base(CSKeyWord.Set) {
        }
        #endregion
    }
    #endregion

    #region Attributes (CSAttribute, CSAttributeGroup, CSAttributeTarget, CSArgument, CSNamedArgument)
    public enum CSAttributeGroupTarget {
        None,
        Field,
        Event,
        Method,
        Module,
        Param,
        Property,
        Return,
        Type,
    }
    public class CSAttribute : TextNodes {

        #region Private Data Members
        private object m_name;
        #endregion

        #region Constructor
        public CSAttribute(object name) {
            m_name = name;
        }
        #endregion

        #region TextNode Override
        protected override IEnumerable<object> Content(TextNodeContext context) {
            return ArbitraryContent;
        }
        protected override IEnumerable<object> Children(TextNodeContext context) {

            return new object[] {
                new CSSubstitution(CSSubstitutionType.AttributeType) { m_name },
                new TextJoin(prefix: "(", suffix: ")") {
                    new CSSubstitution(CSSubstitutionType.Literal) {
                        new TextJoin(", ") { Children() }
                    }
                }
            };
        }
        #endregion
    }
    public class CSAttributeGroup : TextNodes {

        #region Internal Static Members
        public static IEnumerable<object> GroupAttributes(TextNodeContext context, params object[] attributes) {
            return GroupAttributes(context, CSAttributeGroupTarget.None, attributes);
        }
        public static IEnumerable<object> GroupAttributes(TextNodeContext context, CSAttributeGroupTarget target, params object[] attributes) {

            var groups = new Queue<object>();
            groups.Enqueue(null);

            foreach (var attribute in context.Flatten(attributes)) {
                if (attribute is CSAttributeGroup) {
                    groups.Enqueue(attribute);
                    groups.Enqueue(null);

                } else if (attribute is CSAttribute) {
                    var group = groups.Peek() as CSAttributeGroup;
                    if (group == null)
                        groups.Enqueue(group = new CSAttributeGroup(target));
                    group.Add(attribute);

                } else
                    groups.Enqueue(attribute);
            }

            return new TextSingleSpaced() { groups };
        }
        #endregion

        #region Private Data Members
        private object m_target;
        #endregion

        #region Constructor
        public CSAttributeGroup() {
        }
        public CSAttributeGroup(object target) {
            m_target = target;
        }
        #endregion

        #region TextNode Override
        protected override IEnumerable<object> Content(TextNodeContext context) {
            return Children();
        }
        protected override IEnumerable<object> Children(TextNodeContext context) {

            // target
            object target = new TextJoin(suffix: ": ") { new CSSubstitution(CSSubstitutionType.AttributeGroupTarget) { m_target } };

            // content
            var content = new TextJoin(", ") { Children() };

            return new object[] { "[", target, content, "]" };
        }
        #endregion
    }
    public class CSArgument : TextNode {

        #region Private Data Members
        private object m_value;
        #endregion

        #region Constructor
        public CSArgument(object value) {
            m_value = value;
        }
        #endregion

        #region Format Overrides
        protected override IEnumerable<object> Children(TextNodeContext context) {
            return new[] { new CSSubstitution(CSSubstitutionType.Literal) { m_value } };
        }
        #endregion
    }
    public class CSNamedArgument : CSArgument {

        #region Private Data Members
        private object m_name;
        #endregion

        #region Constructor
        public CSNamedArgument(object name, object value)
            : base(value) {
            m_name = name;
        }
        #endregion

        #region Format Overrides
        protected override IEnumerable<object> Content(TextNodeContext context) {
            return base.Children(context);
        }
        protected override IEnumerable<object> Children(TextNodeContext context) {
            return new object[] { m_name, " = ", base.Children(context) };
        }
        #endregion
    }
    public sealed class CSArgumentIf : CSArgument {

        #region Private Data Members
        private bool m_condition;
        #endregion

        #region Constructor
        public CSArgumentIf(bool test, object value)
            : base(value) {
            m_condition = test;
        }
        #endregion

        #region Format Overrides
        protected override IEnumerable<object> Children(TextNodeContext context) {
            if (!m_condition)
                return null;
            return base.Children(context);
        }
        #endregion
    }
    public sealed class CSNamedArgumentIf : CSNamedArgument {

        #region Private Data Members
        private bool m_condition;
        #endregion

        #region Constructor
        public CSNamedArgumentIf(bool test, object name, object value)
            : base(name, value) {
            m_condition = test;
        }
        #endregion

        #region Format Overrides
        protected override IEnumerable<object> Content(TextNodeContext context) {
            if (!m_condition)
                return null;
            return base.Content(context);
        }
        protected override IEnumerable<object> Children(TextNodeContext context) {
            if (!m_condition)
                return null;
            return base.Children(context);
        }
        #endregion
    }
    public sealed class CSAttributeValue : TextNode {

        #region Private Data Members
        private object m_value;
        #endregion

        #region Constructor
        public CSAttributeValue(object value) {
            m_value = value;
        }
        #endregion

        #region TextNode Overrides
        protected override IEnumerable<object> Content(TextNodeContext context) {
            return new[] { m_value };
        }
        protected override IEnumerable<object> Children(TextNodeContext context) {

            if (m_value is Array) {
                var values = ((IEnumerable)m_value).Cast<object>();
                var arrayType = values.Select(o => o.GetType()).Distinct().Count() == 1 ? null : typeof(object);
                yield return new CSArray(arrayType) { values.Select(o => new CSAttributeValue(o)) };

            } else if (m_value is string)
                yield return new CSString(m_value);

            else
                yield return new CSSubstitution(CSSubstitutionType.Literal) { m_value };
        }
        #endregion
    }
    #endregion

    #region Body (CSScope, CSExpressionScope, CSKeyWordExpressionScope, CSUsing, CSIf, CSElseIf, CSWhile)
    public class CSScope : TextNodes {

        #region Private Data Members
        private object m_prefix;
        #endregion

        #region Constructor
        public CSScope() { }
        public CSScope(object prefix) {
            m_prefix = prefix;
        }
        #endregion

        #region TextNode Overrides
        protected override IEnumerable<object> Content(TextNodeContext context) {
            return ArbitraryContent;
        }
        protected override IEnumerable<object> Children(TextNodeContext context) {

            var open = "{";
            var close = Environment.NewLine + "}";


            var prefix = new TextJoin(suffix: " ") { m_prefix };
            if (!context.ContainsContent(Children()))
                return new object[] { prefix, "{ }" };

            var newLineCapture = new TCaptureNewLine() { new TextSingleSpaced() { Children() } };
            var evaluation = context.Evaluate(new CSIndent() { newLineCapture });

            if (CanDropBrackets && !newLineCapture.IsMultiLine) {
                open = close = string.Empty;
                if (InLineSibling)
                    close = Environment.NewLine;
            } else if (InLineSibling)
                close += " ";

            return new object[] {
                prefix, open, Environment.NewLine, 
                evaluation,
                close
            };
        }
        #endregion

        #region Public Members
        public bool InLineSibling { get; set; }
        public bool CanDropBrackets { get; set; }
        #endregion
    }
    public class CSExpressionScope : CSScope {

        #region Private Data Members
        private object m_expression;
        #endregion

        #region Constructor
        public CSExpressionScope(params object[] expression) {
            m_expression = expression;
            CanDropBrackets = true;
        }
        #endregion

        #region TextNode Overrides
        protected override IEnumerable<object> Content(TextNodeContext context) {
            return ArbitraryContent;
        }
        protected override IEnumerable<object> Children(TextNodeContext context) {
            var expression = !context.ContainsContent(m_expression) ? null : new object[] { "(", m_expression, ") " };
            return new object[] { expression, base.Children(context) };
        }
        #endregion

        #region Public Members
        public object KeyWord { get; set; }
        #endregion
    }
    public abstract class CSKeyWordExpressionScope : CSExpressionScope {

        #region Private Data Members
        private object m_keyWord;
        #endregion

        #region Constructor
        public CSKeyWordExpressionScope(object keyWord, object expression)
            : base(expression) {
            m_keyWord = keyWord;
        }
        #endregion

        #region TextNode Override
        protected override IEnumerable<object> Content(TextNodeContext context) {
            return ArbitraryContent;
        }
        protected override IEnumerable<object> Children(TextNodeContext context) {
            return new TextNodes() { 
                m_keyWord, " ", base.Children(context),
            };
        }
        #endregion
    }
    public sealed class CSUsing : CSKeyWordExpressionScope {

        #region Constructor
        public CSUsing(params object[] expression)
            : base(CSKeyWord.Using, expression) {
        }
        #endregion
    }
    public sealed class CSIf : CSKeyWordExpressionScope {

        #region Static Internal Members
        internal new static bool IsContinuation(TextNodeContext context, object predecessor) {
            var isIfOrElseIf = predecessor is CSIf || predecessor is CSElseIf;
            if (!isIfOrElseIf)
                return false;

            var scope = predecessor as CSScope;
            scope.InLineSibling = true;
            return true;
        }
        #endregion

        #region Constructor
        public CSIf(object expression)
            : base(CSKeyWord.If, expression) {
        }
        #endregion
    }
    public sealed class CSElseIf : CSKeyWordExpressionScope {

        #region Constructor
        public CSElseIf(object expression)
            : base(new object[] { CSKeyWord.Else, " ", CSKeyWord.If }, expression) {
        }
        #endregion

        #region TextNode Overrides
        //protected sealed override bool IsContinuation(TextNodeContext context, object predecessor) {
        //    return CSIf.IsContinuation(context, predecessor);
        //}
        #endregion
    }
    public sealed class CSElse : CSKeyWordExpressionScope {

        #region Constructor
        public CSElse()
            : base(CSKeyWord.Else, null) {
        }
        #endregion

        #region TextNode Overrides
        //protected sealed override bool IsContinuation(TextNodeContext context, object predecessor) {
        //    return CSIf.IsContinuation(context, predecessor);
        //}
        #endregion
    }
    public sealed class CSWhile : CSKeyWordExpressionScope {

        #region Constructor
        public CSWhile(object expression)
            : base(CSKeyWord.While, expression) {
        }
        #endregion
    }
    public sealed class CSSwitch : CSKeyWordExpressionScope {

        #region Constructor
        public CSSwitch(object expression)
            : base(CSKeyWord.Switch, expression) {
        }
        #endregion
    }
    public sealed class CSCase : TextNodes {

        #region Private Data Members
        private object m_expression;
        #endregion

        #region Constructor
        public CSCase(object expression) {
            m_expression = expression;
        }
        #endregion

        #region TextNode Overrides
        protected override IEnumerable<object> Content(TextNodeContext context) {
            return ArbitraryContent;
        }
        protected override IEnumerable<object> Children(TextNodeContext context) {
            return new object[] {
                CSKeyWord.Case, " ", m_expression, ":", Environment.NewLine, 
                new CSIndent() { new TextSingleSpaced() { Children() } },
            };
        }
        #endregion
    }
    public sealed class CSDefaultCase : TextNodes {

        #region TextNode Overrides
        protected override IEnumerable<object> Content(TextNodeContext context) {
            return ArbitraryContent;
        }
        protected override IEnumerable<object> Children(TextNodeContext context) {
            return new object[] {
                CSKeyWord.Default, ":", Environment.NewLine, 
                new CSIndent() { new TextSingleSpaced() { Children() } },
            };
        }
        #endregion
    }
    #endregion
#endif
}
